package com.alkaid.user.service.impl;

import com.alkaid.commons.core.CacheKeyDefinition;
import com.alkaid.commons.msg.MsgDefinition;
import com.alkaid.commons.core.ParamDefinition;
import com.alkaid.commons.entity.Authority;
import com.alkaid.commons.entity.Lev;
import com.alkaid.commons.entity.User;
import com.alkaid.commons.ex.AuthorityInsertException;
import com.alkaid.commons.ex.UserInsertException;
import com.alkaid.commons.ex.UserNotFoundException;
import com.alkaid.commons.ex.UserUpdateException;
import com.alkaid.commons.param.user.ChangePasswordParam;
import com.alkaid.commons.utils.Base64Utils;
import com.alkaid.commons.utils.JwtUtils;
import com.alkaid.commons.utils.SnowflakeIdWorker;
import com.alkaid.user.mapper.AuthorityMapper;
import com.alkaid.user.mapper.UserMapper;
import com.alkaid.user.service.AuthorityService;
import com.alkaid.user.service.LevService;
import com.alkaid.user.service.UserService;
import com.alkaid.user.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @author Kylin
 */
@Service
public class UserServiceImpl implements UserService {

    private UserMapper userMapper;

    private LevService levService;

    private AuthorityMapper authorityMapper;

    private AuthorityService authorityService;

    private RedisUtils redisUtils;

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Autowired
    public void setLevService(LevService levService) {
        this.levService = levService;
    }

    @Autowired
    public void setAuthorityMapper(AuthorityMapper authorityMapper) {
        this.authorityMapper = authorityMapper;
    }

    @Autowired
    public void setAuthorityService(AuthorityService authorityService) {
        this.authorityService = authorityService;
    }

    @Autowired
    public void setRedisUtils(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    @Override
    public User getUser(String jwt) {
        return getUserById(getUserByJwt(jwt).getId());
    }

    @Override
    public void reg(String nickname, String password, String mobile, String smsCode) throws UserInsertException {
        // 从缓存读取验证码
        String key = CacheKeyDefinition.SMS_KEY_MAP.get("reg") + mobile;
        String redisCode = (String) redisUtils.getFromRedis(key);
        if (redisCode == null) {
            // 验证码不存在
            throw new UserInsertException(MsgDefinition.SMS_NOLL);
        }
        if (!redisCode.equals(smsCode)) {
            // 验证码错误
            throw new UserInsertException(MsgDefinition.SMS_ERROR);
        }
        if (isNicknameExists(nickname)) {
            // 昵称已存在
            throw new UserInsertException(MsgDefinition.NICKNAME_EXISTS);
        }
        if (userMapper.findUserByUsername(mobile) != null) {
            // 手机号已注册
            throw new UserInsertException(MsgDefinition.USERNAME_EXISTS);
        }
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        Long id = idWorker.nextId();
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String defaultAvatar = "/img/user/default.jpg";
        User user = new User();
        user.setId(id);
        user.setUsername(mobile);
        user.setPassword(passwordEncoder.encode(password));
        user.setNickname(nickname);
        user.setAvatar(defaultAvatar);
        user.setStatus(0);
        user.setExp(20);
        user.setCoins(20f);
        user.setCreatedUser(mobile);
        user.setCreatedTime(new Date());
        user.setModifiedUser(mobile);
        user.setModifiedTime(new Date());
        Integer result = userMapper.insertUser(user);
        if (result < 1) {
            throw new UserInsertException(MsgDefinition.REG_FAILED);
        }
        authorityService.addAuthorityForReg(id, mobile);
    }

    @Override
    public void setUserInfo(String jwt, User user) throws UserUpdateException {
        Long id = getUserByJwt(jwt).getId();
        User userDO = getUserById(id);
        Float coins = userDO.getCoins();
        // 查询硬币是否足够
        int min = 6;
        if (coins < min) {
            throw new UserUpdateException(MsgDefinition.COINS_LOSS);
        }
        String nickname = user.getNickname();
        // 检查昵称是否修改
        if (!nickname.equals(userDO.getNickname())) {
            // 查询昵称是否已存在
            if (isNicknameExists(nickname)) {
                throw new UserUpdateException(MsgDefinition.NICKNAME_EXISTS);
            }
            userDO.setCoins(coins - 6);
        }
        userDO.setNickname(nickname);
        // 存入性别信息
        Integer sex = user.getSex();
        userDO.setSex(sex);
        userDO.setSignature(user.getSignature());
        userDO.setBirth(user.getBirth());
        userDO.setModifiedUser(userDO.getUsername());
        userDO.setModifiedTime(new Date());
        Integer result = userMapper.updateUserInfo(userDO);
        if (result <= 0) {
            throw new UserUpdateException(MsgDefinition.INFO_UPDATE_ERROR);
        }
    }

    @Override
    public boolean checkNickname(String nickname) {
        return !isNicknameExists(nickname);
    }

    @Override
    public void setAvatar(String jwt, String avatar) throws UserNotFoundException, UserUpdateException {
        String regex = "^\\s*data:image/jpeg;base64,([a-z0-9A-Z!$&',()*+;=\\-._~:@/?%\\s]*?)\\s*$";
        if (!avatar.matches(regex)) {
            return;
        }
        JwtUtils jwtUtils = new JwtUtils();
        jwtUtils.parseJwt(jwt);
        Long id = jwtUtils.getUser().getId();
        User user = userMapper.findUserById(id);
        if (user == null) {
            // 用户不存在
            throw new UserNotFoundException(MsgDefinition.USER_NOT_REG);
        }
        // base64转图片
        boolean result = Base64Utils.base64StrToImage(avatar, ParamDefinition.getAvatarPath(id));
        if (!result) {
            // 头像修改失败
            throw new UserUpdateException(MsgDefinition.AVATAR_UPDATE_ERROR);
        }
        user.setAvatar(ParamDefinition.getAvatarHttpPath(id));
        // 将更改存入数据库
        Integer ok = userMapper.updateAvatar(user);
        if (ok < 1) {
            throw new UserUpdateException(MsgDefinition.AVATAR_UPDATE_ERROR);
        }
    }

    @Override
    public void changePassword(Long id, ChangePasswordParam param) throws UserUpdateException {
        String key = CacheKeyDefinition.SMS_KEY_MAP.get("forget") + param.getMobile();
        String value = (String) redisUtils.getFromRedis(key);
        if (value == null || "".equals(value)) {
            throw new UserUpdateException("你还没有请求发送验证码！");
        }
        if (!value.equals(param.getSmsCode())) {
            throw new UserUpdateException("验证码错误！");
        }
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode(param.getPassword());
        User user = new User();
        user.setId(id);
        user.setPassword(password);
        Integer result = userMapper.updatePassword(user);
        if (result < 1) {
            throw new UserUpdateException("修改密码失败，请稍后重试！");
        }
    }

    @Override
    public String pay(Long id) throws AuthorityInsertException {
        List<Authority> authorityList = authorityService.getAuthoritiesByUserId(id);
        Integer result;
        String vip = "VIP";
        if (vip.equals(authorityList.get(0).getAuthority())) {
            Authority select = authorityList.get(0);
            select.setExpiredTime(addMonth(select.getExpiredTime()));
            select.setModifiedTime(new Date());
            result = authorityMapper.updateById(select);
        } else {
            Authority authority = new Authority();
            SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 4);
            authority.setId(idWorker.nextId());
            authority.setUserId(id);
            authority.setRoleId(3);
            authority.setExpiredTime(addMonth(new Date()));
            authority.setModifiedTime(new Date());
            result = authorityMapper.insert(authority);
            // 更新用户标签
            User user = new User();
            user.setId(id);
            user.setTag(1);
            userMapper.updateById(user);
        }
        if (result < 1) {
            throw new AuthorityInsertException("充值失败");
        }
        User user = userMapper.findUserById(id);
        List<Authority> authorities = authorityMapper.findAuthorityByUserId(id);
        user.setAuthorities(authorities);
        JwtUtils jwtUtils = new JwtUtils();
        return jwtUtils.createJwt(user, false);
    }

    private User getUserByJwt(String jwt) {
        JwtUtils jwtUtils = new JwtUtils();
        jwtUtils.parseJwt(jwt);
        return jwtUtils.getUser();
    }

    /**
     * 通过用户ID查询用户信息
     *
     * @param id 用户ID
     * @return 用户信息
     * @throws UserNotFoundException 用户不存在
     */
    private User getUserById(Long id) throws UserNotFoundException {
        User user = userMapper.findUserById(id);
        if (user == null) {
            throw new UserNotFoundException("用户名不存在");
        }
        user.setVipExpiredTime(userMapper.findVipExpiredTime(id));
        List<Lev> levList = levService.getLevList();
        Lev lev = levList.get(0);
        levList.add(0, lev);
        int i = 1, length = levList.size();
        while (i++ < length) {
            Lev levDO = levList.get(i);
            if (user.getExp() >= levDO.getMinExp()) {
                user.setLev(levList.get(i - 1).getId());
                user.setTotalExp(levList.get(i - 1).getMinExp());
                break;
            }
        }
        List<Authority> list = authorityService.getAuthoritiesByUserId(id);
        user.setAuthorities(list);
        return user;
    }

    /**
     * 查询昵称是否存在
     *
     * @param nickname 昵称
     * @return boolean
     */
    private boolean isNicknameExists(String nickname) {
        User user = userMapper.findNickname(nickname);
        return user != null;
    }

    private Date addMonth(Date date) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(date);
        cd.add(Calendar.MONTH, 1);
        return cd.getTime();
    }

}
